package window;

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JTree;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;

import main.DataConnection;
import main.Its;
import main.ItsChooser;
import main.ResultSet;
import main.ResultSet.ItsSet;

public class MainWindow {

	private JFrame frmIdss;
	private ItsChooser itc = null;
	private DefaultTreeModel treeModel;
	private GraphFrame radarGraph;
	private final ButtonGroup buttonGroup = new ButtonGroup();
	private ArrayList<ProblemControls> problCtrls = new ArrayList<ProblemControls>();
	private int tmpCounter = 0;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					MainWindow window = new MainWindow();
					ProblemsSideBar psb = new ProblemsSideBar(window);
					window.frmIdss.setVisible(true);
					psb.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the application.
	 */
	public MainWindow() {
		initialize();

		// Place window in the middle of the screen
		Toolkit tools = Toolkit.getDefaultToolkit();
		Dimension screenSize = tools.getScreenSize();
		frmIdss.setLocation((screenSize.width - frmIdss.getWidth()) / 2,
				(screenSize.height - frmIdss.getHeight()) / 2);
	}

	/**
	 * Initialize the contents of the frame.
	 */
	private void initialize() {
		frmIdss = new JFrame();
		frmIdss.setTitle("IDSS");
		frmIdss.setBounds(100, 100, 700, 549);
		frmIdss.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frmIdss.getContentPane().setLayout(null);

		radarGraph = new GraphFrame();
		radarGraph.setVisible(true);
		addProblemControls("Emission");
		addProblemControls("Accidents");
		addProblemControls("Noise and related external costs");

		// / Year ctrl init
		JLabel lblInYear = new JLabel("In year");
		lblInYear.setBounds(10, 33, 61, 14);
		frmIdss.getContentPane().add(lblInYear);

		final JSlider sldrYear = new JSlider();
		sldrYear.setMinorTickSpacing(1);
		sldrYear.setMaximum(2051);
		sldrYear.setMinimum(2011);
		sldrYear.setPaintLabels(true);
		sldrYear.setMajorTickSpacing(10);
		sldrYear.setBounds(80, 22, 200, 37);
		frmIdss.getContentPane().add(sldrYear);

		final JLabel lblYearVal = new JLabel("2011");
		lblYearVal.setBounds(299, 33, 55, 14);
		frmIdss.getContentPane().add(lblYearVal);

		sldrYear.addChangeListener(new ChangeListener() {
			public void stateChanged(ChangeEvent e) {
				if (!sldrYear.getValueIsAdjusting()) {
					lblYearVal.setText(String.valueOf(sldrYear.getValue()));
				}
			}
		});
		// // End Year ctrl

		treeModel = new DefaultTreeModel(buildTree(null));

		final JTree tree = new JTree(treeModel);
		MouseListener ml = produceMouseListener(tree);
		tree.addMouseListener(ml);
		JScrollPane scroll = new JScrollPane(tree);
		scroll.setBounds(484, 0, 200, 478);
		frmIdss.getContentPane().add(scroll);

		final int startLblX = 370;
		final int rbtnWidth = 100;
		final int rbtnHeight = 23;
		JLabel lblSortBy = new JLabel("Sort by:");
		lblSortBy.setBounds(startLblX, 2, 46, 14);
		frmIdss.getContentPane().add(lblSortBy);

		final JRadioButton rdbtnOverallPerformance = new JRadioButton(
				"performance");
		buttonGroup.add(rdbtnOverallPerformance);
		rdbtnOverallPerformance.setBounds(startLblX, 70, rbtnWidth, rbtnHeight);
		frmIdss.getContentPane().add(rdbtnOverallPerformance);

		final JRadioButton rdbtnEfficiency = new JRadioButton("efficiency");
		buttonGroup.add(rdbtnEfficiency);
		rdbtnEfficiency.setSelected(true);
		rdbtnEfficiency.setBounds(startLblX, 29, rbtnWidth, rbtnHeight);
		frmIdss.getContentPane().add(rdbtnEfficiency);

		final Its itsList[] = DataConnection.readData();
		itc = new ItsChooser(itsList);

		JButton btnCalculate = new JButton("Calculate");
		btnCalculate.setBounds(startLblX, 119, 89, 23);
		frmIdss.getContentPane().add(btnCalculate);

		// "Calculate" action listener
		btnCalculate.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent arg0) {
				Map<String, Double> goals = new HashMap<String, Double>();
				for (ProblemControls pc : problCtrls) {
					goals.put(pc.getName(), pc.getSliderValue() / 100.0);
				}
				ResultSet results = itc.getResults(goals, sldrYear.getValue());
				if (rdbtnEfficiency.isSelected())
					results.sort(goals, ResultSet.BY_EFFICIENCY);
				else if (rdbtnOverallPerformance.isSelected())
					results.sort(goals, ResultSet.BY_OVER_PERF);

				treeModel.setRoot(buildTree(results));
				for (int i = 0; i < 3; i++)
					tree.expandRow(i);
			}
		});

		// if Radar inside the panel
		// ChartPanel panel = radarGraph.getPanel();
		// frmIdss.getContentPane().add(panel);

		JMenuBar menuBar = new JMenuBar();
		frmIdss.setJMenuBar(menuBar);

		JMenu mnItsAdd = new JMenu("ITS");
		menuBar.add(mnItsAdd);

		JMenu mnProbAdd = new JMenu("Problems");
		menuBar.add(mnProbAdd);

		JMenuItem addProblem = new JMenuItem("Add");
		JMenuItem modifyProblem = new JMenuItem("Modify");
		JMenuItem removeProblem = new JMenuItem("Remove");

		addProblem.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				java.awt.EventQueue.invokeLater(new Runnable() {

					@Override
					public void run() {
						AddProblemWindow aiw = new AddProblemWindow();
						aiw.setLocation(frmIdss.getLocation());
						aiw.setVisible(true);
					}
				});
			}
		});

		modifyProblem.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				java.awt.EventQueue.invokeLater(new Runnable() {

					@Override
					public void run() {
						ModifyProblemWindow aiw = new ModifyProblemWindow();
						aiw.setLocation(frmIdss.getLocation());
						aiw.setVisible(true);
					}
				});
			}
		});

		removeProblem.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				java.awt.EventQueue.invokeLater(new Runnable() {

					@Override
					public void run() {
						RemoveElementWindow rew = new RemoveElementWindow(
								RemoveElementWindow.PROBLEM);
						rew.setLocation(frmIdss.getLocation());
						rew.setVisible(true);
					}
				});
			}
		});

		mnProbAdd.add(addProblem);
		mnProbAdd.add(modifyProblem);
		mnProbAdd.add(removeProblem);

		JMenuItem mntmAddIts = new JMenuItem("Add");
		mntmAddIts.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				java.awt.EventQueue.invokeLater(new Runnable() {

					@Override
					public void run() {
						AddItsWindow aiw = new AddItsWindow();
						aiw.setLocation(frmIdss.getLocation());
						aiw.setVisible(true);
					}
				});

			}
		});
		mnItsAdd.add(mntmAddIts);

		JMenuItem mntmModifyIts = new JMenuItem("Modify");
		mntmModifyIts.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				java.awt.EventQueue.invokeLater(new Runnable() {

					@Override
					public void run() {
						ItsCharacteristicsWindow icw = new ItsCharacteristicsWindow();
						icw.setLocation(frmIdss.getLocation());
						icw.setVisible(true);
					}
				});
			}
		});
		mnItsAdd.add(mntmModifyIts);

		JMenuItem mntmRemoveIts = new JMenuItem("Remove");
		mntmRemoveIts.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				java.awt.EventQueue.invokeLater(new Runnable() {

					@Override
					public void run() {
						RemoveElementWindow rew = new RemoveElementWindow(
								RemoveElementWindow.ITS);
						rew.setLocation(frmIdss.getLocation());
						rew.setVisible(true);
					}
				});
			}
		});
		mnItsAdd.add(mntmRemoveIts);

		JMenuItem mntmHelp = new JMenuItem("Help");
		menuBar.add(mntmHelp);

		JMenuItem mntmAbout = new JMenuItem("About");
		menuBar.add(mntmAbout);

	}

	public ArrayList<ProblemControls> getProblCtrls() {
		return problCtrls;
	}

	public void addProblemControls(String probName) {

		for (ProblemControls pc : problCtrls) {
			if (pc.getName().equalsIgnoreCase(probName))
				return;
		}
		problCtrls.add(new ProblemControls(probName));
		frmIdss.getContentPane().repaint();
	}

	public void updateProblemControls(ArrayList<String> probToUpdate) {
		HashMap<String, Integer> oldVals = removeProblemControls();
		for (String probName : probToUpdate) {
			problCtrls.add(new ProblemControls(probName));
		}

		restoreOldValues(oldVals);

		frmIdss.getContentPane().repaint();
	}

	private void restoreOldValues(HashMap<String, Integer> probMap) {
		for (Map.Entry<String, Integer> entry : probMap.entrySet()) {
			for (ProblemControls pc : problCtrls) {
				if (pc.getName().equals(entry.getKey())) {
					pc.setSliderValue(entry.getValue());
					break;
				}
			}
		}
	}

	private HashMap<String, Integer> removeProblemControls() {
		HashMap<String, Integer> probMap = new HashMap<String, Integer>();
		for (int i = 0; i < problCtrls.size(); i++) {
			probMap.put(problCtrls.get(i).getName(), problCtrls.get(i)
					.getSliderValue());
			problCtrls.get(i).clear();
		}
		problCtrls.clear();
		radarGraph.resetData();
		tmpCounter = 0;
		return probMap;
	}

	public String printIntTab(int[] tab) {
		String result = "";
		for (int i : tab) {
			result += i + " ";
		}
		return result;
	}

	private void updateChartModel(int i, int j, double value) {
		// frmIdss.getContentPane().remove(radarGraph.getPanel());
		radarGraph.updateChartModel(i, j, value);
		// ChartPanel panel = radarGraph.getPanel();
		// frmIdss.getContentPane().add(panel);
		// frmIdss.getContentPane().repaint();
	}

	private void updateChartModel(int i, Map<String, Double> results) {
		radarGraph.updateChartModel(i, results);
	}

	DefaultMutableTreeNode buildTree(ResultSet results) {
		if (treeModel == null) {
			DefaultMutableTreeNode empty = new DefaultMutableTreeNode("EMPTY");
			return empty;
		}
		DefaultMutableTreeNode solutions = new DefaultMutableTreeNode(
				"Solutions");
		if (results.isPolicy()) {
			DefaultMutableTreeNode best = new DefaultMutableTreeNode("Policy");
			solutions.add(best);
			DefaultMutableTreeNode set = new DefaultMutableTreeNode();
			set.setUserObject(results.get(0));
			best.add(set);
			return solutions;
		}

		DefaultMutableTreeNode best = new DefaultMutableTreeNode("Proposed ITS");
		solutions.add(best);
		DefaultMutableTreeNode rest = new DefaultMutableTreeNode("Rest ITS");
		solutions.add(rest);

		results.isPolicy();
		int count = 0;
		int j = 1;
		for (int i = 0; i < results.getSize(); i++) {
			DefaultMutableTreeNode set = new DefaultMutableTreeNode();
			set.setUserObject(results.get(i));
			if (j++ <= 3)
				best.add(set);
			else {
				rest.add(set);
			}
		}

		if (count == 0)
			rest.add(new DefaultMutableTreeNode(new String("EMPTY")));
			return solutions;
	}

	MouseListener produceMouseListener(final JTree tree) {
		return new MouseAdapter() {
			public void mousePressed(MouseEvent e) {
				int selRow = tree.getRowForLocation(e.getX(), e.getY());
				TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
				if (selRow != -1) {
					if (e.getClickCount() == 1) {
						drawItsOnRadar(selPath);
					} else if (e.getClickCount() == 2) {
						showDetails(selPath);
					}
				}
			}

			private void showDetails(TreePath selPath) {
				DefaultMutableTreeNode dmtn = (DefaultMutableTreeNode) selPath
						.getLastPathComponent();
				ItsSet itsSet = (ItsSet) dmtn.getUserObject();
				String details = "Set " + itsSet.printDetails() + "\n";
				details += "contains:\n";
				for (int k = 0; k < itsSet.getSize(); k++) {
					int idx = itsSet.getSet()[k];
					Its tmp = itc.getItsList()[idx];
					details += tmp.getName() + ", ";
				}
				JOptionPane.showMessageDialog(frmIdss, details);
			}

			private void drawItsOnRadar(TreePath selPath) {
				DefaultMutableTreeNode dmtn = (DefaultMutableTreeNode) selPath
						.getLastPathComponent();
				ItsSet itsSet = (ItsSet) dmtn.getUserObject();
				System.out
						.println("Single Click on-> " + itsSet.printDetails());
				updateChartModel(2, itsSet.getPerformance());
			}
		};
	}

	public class ProblemControls {
		private final JSlider slider;
		private final JLabel lblValue;
		private final JLabel lblName;
		private final int goalIdxInModel = 1;
		private final int txtLblWidth = 61;
		private final int valLblWidth = 30;
		private final int sliderWidth = 200;
		private final int sliderHeight = 37;
		private final int lblHeight = 14;
		private final int txtLblX = 10;
		private final int valLblX = 299;
		private final int sliderX = 80;
		private final int startLblY = 80;
		private final int startSliderY = 70;
		private final int lblVerticalPadding = 50;

		/**
		 * Class that packs slider and labels into one ProblemControl object
		 * 
		 * @param problemName
		 */
		public ProblemControls(String problemName) {
			lblName = createTextLabel(problemName, txtLblX, getLblY());
			slider = createSlider(0, 100, sliderX, getSliderY());
			lblValue = createValueLabel("0%", valLblX, getLblY());
			int probIdx = radarGraph.getProbIdx(problemName);
			System.out.println("idx = " + problemName + " " + probIdx);
			addSliderListener(goalIdxInModel, probIdx);

			frmIdss.getContentPane().add(lblName);
			frmIdss.getContentPane().add(slider);
			frmIdss.getContentPane().add(lblValue);
			tmpCounter++;
		}

		private int getLblY() {
			return startLblY + tmpCounter * lblVerticalPadding;
		}

		private int getSliderY() {
			return startSliderY + tmpCounter * lblVerticalPadding;
		}

		private void addSliderListener(final int probNo, final int inflNo) {
			slider.addChangeListener(new ChangeListener() {
				public void stateChanged(ChangeEvent arg0) {
					if (!slider.getValueIsAdjusting()) {
						lblValue.setText(String.valueOf(slider.getValue())
								+ "%");
						updateChartModel(probNo, inflNo, slider.getValue());
					}
				}

			});

		}

		private JLabel createTextLabel(String text, int x, int y) {
			JLabel label = new JLabel(text);
			label.setToolTipText(text);
			label.setBounds(x, y, txtLblWidth, lblHeight);

			return label;
		}

		private JLabel createValueLabel(String txt, int x, int y) {
			JLabel label = new JLabel(txt);
			label.setBounds(x, y, valLblWidth, lblHeight);

			return label;
		}

		private JSlider createSlider(int min, int max, int x, int y) {
			JSlider slider = new JSlider();
			slider.setValue(0);
			slider.setPaintLabels(true);
			slider.setMinimum(min);
			slider.setMaximum(max);
			slider.setMajorTickSpacing(10);
			slider.setMinorTickSpacing(1);
			slider.setBounds(x, y, sliderWidth, sliderHeight);

			return slider;
		}

		public int getSliderValue() {
			return slider.getValue();
		}

		public void setSliderValue(int val) {
			slider.setValue(val);
		}

		public String getName() {
			return lblName.getText();
		}

		public void clear() {
			frmIdss.getContentPane().remove(lblName);
			frmIdss.getContentPane().remove(slider);
			frmIdss.getContentPane().remove(lblValue);
			tmpCounter--;
		}

		public boolean equals(Object obj) {
			ProblemControls pc = (ProblemControls) obj;
			return this.getName().equalsIgnoreCase(pc.getName());
		}

	}

}
